package is.api.demo.isgateway.utils;


import is.api.demo.isgateway.support.CipherConstants;
import is.api.demo.isgateway.support.Sm2KeyParameter;
import is.api.demo.isgateway.support.Sm2Support;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.JCEECPrivateKey;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.util.encoders.Hex;

import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
import java.util.Arrays;


/**
 * sm2密钥工具类
 *
 * @author chen.chen
 */
public abstract class Sm2KeyUtils {


    /**
     * 解析SM2 类型的公钥,并返回04 + X||Y类型16进制公钥字符串,不压缩
     *
     * @param publicKey 公钥对象
     * @return 私钥字符串, 16进制
     */
    public static String getPubHex(PublicKey publicKey) {
        BCECPublicKey bcPublicKey = (BCECPublicKey) publicKey;
        return new String(Hex.encode(bcPublicKey.getQ().getEncoded(false)));
    }

    /**
     * 解析SM2 类型的私钥,并返回16进制私钥字符串
     *
     * @param privateKey 私钥对象
     * @return 私钥字符串, 16进制
     */
    public static String getPriHex(PrivateKey privateKey) {
        BCECPrivateKey bcPrivateKey = (BCECPrivateKey) privateKey;
        return bcPrivateKey.getD().toString(16);
    }

    /**
     * 获取密钥对,16进制字符串
     *
     * @return SM2密钥参数对象
     * @author chen.chen
     */
    public static Sm2KeyParameter getKeyPairParameter() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
        //获取密钥生成器
        KeyPairGenerator generator = Sm2KeyGenerateFactory.generator();
        //生成随机密钥对
        KeyPair keyPair = generator.generateKeyPair();
        //获取到公钥和私钥
        PublicKey aPublic = keyPair.getPublic();
        PrivateKey aPrivate = keyPair.getPrivate();
        //转为16进制字符串
        String pubHex = getPubHex(aPublic);
        String priHex = getPriHex(aPrivate);
        //创建一个密钥存储对象
        return new Sm2KeyParameter(priHex, pubHex);
    }


    /**
     * 解析SM2 类型的公钥，并构造公钥对象
     *
     * @param publicKeyBytes 公钥字节串 格式为 04 + X || Y
     * @return 公钥对象
     */
    public static ECPublicKey parseSM2PublicKey(byte[] publicKeyBytes) {
        if (publicKeyBytes == null || publicKeyBytes.length == 0) {
            throw new IllegalArgumentException("publicKeyBytes is blank!");
        }
        byte[] pX = new byte[32];
        byte[] pY = new byte[32];
        // 从公钥字节中取出两个坐标值字节,忽略04
        System.arraycopy(publicKeyBytes, 1, pX, 0, 32);
        System.arraycopy(publicKeyBytes, 33, pY, 0, 32);
        // 将坐标值字节转为大数字
        BigInteger bnX = new BigInteger(1, pX);
        BigInteger bnY = new BigInteger(1, pY);
        // 构造公钥点坐标 Pa = （Xa, Ya）
        ECPoint pubPoint = new ECPoint(bnX, bnY);
        // 构造椭圆曲线公钥
        java.security.spec.ECPublicKeySpec ecPublicKeySpec = new java.security.spec.ECPublicKeySpec(pubPoint, Sm2Support.SM2_SPEC);
        return new JCEECPublicKey(CipherConstants.SM2_ALGORITHM, ecPublicKeySpec);
    }

    /**
     * 解析SM2 类型的私钥数据，并且构造私钥对象
     *
     * @param privateKeyBytes 私钥字节串
     * @return 私钥对象
     */
    public static ECPrivateKey parseSM2PrivateKey(byte[] privateKeyBytes) {
        if (privateKeyBytes == null || privateKeyBytes.length == 0) {
            throw new IllegalArgumentException("privateKeyBytes is blank!");
        }
        byte[] pD = new byte[32];
        // 从私钥数据中截取出私钥数据 k， k 属于 [1, n - 1]
        System.arraycopy(privateKeyBytes, privateKeyBytes.length - 32, pD, 0, 32);
        BigInteger bnD = new BigInteger(1, pD);
        // 构造椭圆曲线私钥
        java.security.spec.ECPrivateKeySpec ecPriSpec = new java.security.spec.ECPrivateKeySpec(bnD, Sm2Support.SM2_SPEC);
        return new JCEECPrivateKey(CipherConstants.SM2_ALGORITHM, ecPriSpec);
    }


    /**
     * 获取 X||Y 格式的公钥字节串
     *
     * @param publicKey 公钥对象
     * @return X||Y 格式的公钥字节串
     */
    public static byte[] getPurePubKey(PublicKey publicKey) throws IOException {
        ASN1Sequence sequence = ASN1Sequence.getInstance(publicKey.getEncoded());
        // 取出公钥所在字段
        byte[] asn1PubKeySrc = sequence.getObjectAt(1).toASN1Primitive().getEncoded();
        DERBitString pubKeyBitString = DERBitString.getInstance(asn1PubKeySrc);
        byte[] puk = pubKeyBitString.getBytes();
        // 公钥格式为 04||X||Y (04为16进制字符，参考《GM/64-2012》)
        return Arrays.copyOfRange(puk, 1, puk.length);
    }

}
